package com.liry.mybatis.datascope;

import com.liry.mybatis.datascope.annotation.DataScope;
import com.liry.mybatis.datascope.model.DataColumnProperty;
import com.liry.mybatis.datascope.model.DataScopeProperty;
import org.springframework.core.NamedThreadLocal;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 数据权限工具
 *
 * @author ALI
 * @since 2022/9/14
 */
public class DataScopeUtil {

    public static final String DEFAULT_KEY = "default";
    public static final DataScopeProperty empty = new DataScopeProperty();

    /** 数据权限缓存 */
    private static final ConcurrentHashMap<String, DataScopeProperty> DATA_SCOPE_CACHE = new ConcurrentHashMap<>(16);
    /** 全局缓存 */
    private static final ConcurrentHashMap<String, DataScopeProperty> GLOBAL_SCOPE_CACHE = new ConcurrentHashMap<>(8);
    /** 全局的不确定字段 */
    private static final ConcurrentHashMap<String, DataScopeProperty> GLOBAL_SCOPE_LIKE_CACHE = new ConcurrentHashMap<>(
        8);
    /** 当前请求线程 */
    private static final NamedThreadLocal<DataScopeProperty> THREAD_GLOBAL_DATA_SCOPE_CACHE
        = new NamedThreadLocal<>("currentUser");

    /**
     * 根据mapper方法id获取数据权限
     * 包含：
     * 1. 方法权限
     * 2. 全局权限
     *
     * @param id mapper id
     * @return 权限对象
     */
    public static DataScopeProperty getDataScopeProperty(String id) throws ClassNotFoundException {
        DataScopeProperty data = DATA_SCOPE_CACHE.get(id);
        DataScopeProperty defualt = GLOBAL_SCOPE_CACHE.get(DEFAULT_KEY);
        if (null == data) {
            String entityClassName = getClassName(id);
            if (null == entityClassName) {
                return null;
            }
            data = DATA_SCOPE_CACHE.get(entityClassName);
            if (empty == data) {
                return mergeProperty(data, defualt);
            }

            if (null == data) {
                Class<?> clazz = Class.forName(entityClassName);
                Method[] methods = clazz.getMethods();

                for (Method method : methods) {
                    DataScope dataScope = method.getAnnotation(DataScope.class);
                    if (null != dataScope) {
                        DataScopeProperty temp = new DataScopeProperty(dataScope);
                        DATA_SCOPE_CACHE.put(entityClassName + "." + method.getName(), mergeProperty(temp, defualt));
                    }
                }

                DataScope dataScope = clazz.getAnnotation(DataScope.class);
                DataScopeProperty temp = empty;
                if (dataScope != null) {
                    temp = new DataScopeProperty(dataScope);
                }
                DATA_SCOPE_CACHE.put(entityClassName, mergeProperty(temp, defualt));
                data = DATA_SCOPE_CACHE.get(id);
                if (null == data) {
                    data = DATA_SCOPE_CACHE.get(entityClassName);
                }
            }
        }
        return mergeProperty(data, defualt);
    }

    /**
     * @param data    当前的数据权限
     * @param defualt 基础数据权限
     */
    private static DataScopeProperty mergeProperty(DataScopeProperty data, DataScopeProperty defualt) {
        DataScopeProperty result = new DataScopeProperty();
        if (defualt != null && data != null) {
            Set<DataColumnProperty> collect;
            if (data.getColumns() == null) {
                collect = new HashSet<>();
            } else {
                collect = new HashSet<>(data.getColumns());
            }
            if (defualt.getColumns() != null) {
                collect.addAll(defualt.getColumns());
            }
            result.setColumns(collect);
            result.setIgnore(data.isIgnore());
            result.setType(data.getType());
            return result;
        }
        return data;
    }

    /**
     * 获取全局权限
     */
    public static List<DataScopeProperty> getGlobalProperty() {
        return new ArrayList<>(GLOBAL_SCOPE_CACHE.values());
    }

    /**
     * 设置全局权限
     */
    public static void putGlobalProperty(DataScopeProperty property) {
        putGlobalProperty(DEFAULT_KEY, property);
    }

    /**
     * 设置全局权限
     */
    public static void putGlobalProperty(String key, DataScopeProperty property) {
        GLOBAL_SCOPE_CACHE.put(key, property);
    }

    /**
     * 获取全局不确定的数据字段权限
     */
    public static DataScopeProperty getGlobalLikeProperty() {
        return GLOBAL_SCOPE_LIKE_CACHE.get(DEFAULT_KEY);
    }

    /**
     * 设置全局不确定的数据字段权限
     */
    public static void putGlobalLikeProperty(DataScopeProperty data) {
        GLOBAL_SCOPE_LIKE_CACHE.put(DEFAULT_KEY, data);
    }

    /**
     * 获取线程及数据权限（该权限指挥在当前线程生效）
     */
    public static DataScopeProperty getThreadDataScopeProperty() {
        return THREAD_GLOBAL_DATA_SCOPE_CACHE.get();
    }

    /**
     * 添加线程及数据权限
     *
     * @param data 权限对象
     */
    public static void putThreadDataScopeProperty(DataScopeProperty data) {
        THREAD_GLOBAL_DATA_SCOPE_CACHE.set(data);
    }

    /**
     * 销毁线程权限缓存
     * 线程结束时，请调用该方法
     */
    public static void destroyThread() {
        THREAD_GLOBAL_DATA_SCOPE_CACHE.remove();
    }

    /**
     * 添加缓存
     *
     * @param id       mapper id,方法全名
     * @param property 数据权限对象
     */
    public static void put(String id, DataScopeProperty property) {
        if (StringUtils.isEmpty(id)) {
            throw new RuntimeException("数据权限缓存的key不能为空");
        }
        DATA_SCOPE_CACHE.put(id, property);
    }

    /**
     * 获取mapper class name
     *
     * @param name mapper映射的id
     */
    public static String getClassName(String name) {
        // 重新new一个
        String newName = new String(name);
        if (name.contains("_")) {
            newName = name.substring(0, name.indexOf("_"));
        }
        int mapperName = newName.lastIndexOf(".");
        return mapperName > 0 ? newName.substring(0, newName.lastIndexOf(".")) : null;
    }

}
